package server;

import java.net.InetAddress;
import java.rmi.*;
import java.rmi.server.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;

import client.game.TicTacToeButton;

import model.*;
import model.exceptions.*;

import common.*;

public class ServerController extends UnicastRemoteObject implements
		ServerInterface {
	private ArrayList<Client> clients;
	private ArrayList<Game> games;
	private ServerFrame frame;
	private ServerButton button;
	private String user;
	private Message message, chat;
	private boolean newMessage, newChat, newButton;

	public ServerController() throws RemoteException {
		super();
		clients = new ArrayList<Client>();
		games = new ArrayList<Game>();
		newMessage = false;
		newChat = false;
		newButton = false;
	}

	public void begin(ServerFrame frame) throws Exception {
		this.frame = frame;

		frame.println("Server listening on "
				+ InetAddress.getLocalHost().getHostAddress());
		new MessageThread().start();
		new ChatThread().start();
		new ButtonThread().start();
	}

	@Override
	public synchronized ArrayList<String> getAllUsers() throws RemoteException {
		ArrayList<String> list = new ArrayList<String>();

		Iterator<Client> it = clients.iterator();
		while (it.hasNext()) {
			Client user = it.next();
			if (user.getInterface() != null)
				list.add(user.getName());
		}

		return list;
	}

	protected void userLoggedin(String name) {
		Iterator<Client> it = clients.iterator();
		while (it.hasNext())
			try {
				ClientInterface client = it.next().getInterface();
				if (client != null)
					client.userLoggedin(name);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
	}

	protected void userLoggedout(String name) {
		Iterator<Client> it = clients.iterator();
		while (it.hasNext())
			try {
				ClientInterface client = it.next().getInterface();
				if (client != null)
					client.userLoggedout(name);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
	}

	@Override
	public synchronized void login(String name, String password,
			ClientInterface clientController) throws RemoteException,
			AbstractException {
		Client user = findUser(name);
		if (user == null)
			throw new WrongUsernameException();
		user.login(password, clientController);
		userLoggedin(name);
	}

	@Override
	public synchronized void logout(String name) throws RemoteException {
		findUser(name).logout();
		userLoggedout(name);
	}

	@Override
	public synchronized void registerUser(String name, String password,
			ClientInterface clientController) throws RemoteException,
			AbstractException {
		if (findUser(name) != null)
			throw new UserAlreadyExistsException();
		clients.add(new Client(name, password, clientController));
		userLoggedin(name);
	}

	@Override
	public synchronized void startGame(String opponentName, String name)
			throws RemoteException, AbstractException {
		Client opponent = findUser(opponentName);
		opponent.getInterface().startConversation(name);

		games.add(new Game(findUser(name), opponent, this));
	}

	@Override
	public synchronized void stopGame(String name) throws RemoteException {
		Client thisUser = findUser(name);
		Game conversation = findGame(thisUser);
		conversation.stop(thisUser);
		games.remove(conversation);
	}

	public void endGameOf(Client user) {
		try {
			user.getInterface().endConversation();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	private Game findGame(Client user) {
		Iterator<Game> it = games.iterator();
		while (it.hasNext()) {
			Game conversation = it.next();
			if (conversation.containsUser(user))
				return conversation;
		}
		return null;
	}

	private Client findUser(String name) {
		Iterator<Client> it = clients.iterator();
		while (it.hasNext()) {
			Client user = it.next();
			if (user.getName().equals(name))
				return user;
		}
		return null;
	}


	@Override
	public User[] getHighScores() throws RemoteException {
		User[] user=new User[clients.size()];
		Iterator<Client> it=clients.iterator();
		int i=0;
		while(it.hasNext()) {
			Client client=it.next();
			user[i]=new User(client.getName(), client.getScore());
			i++;
		}
		return user;
	}
	
	@Override
	public synchronized void takeMessage(Message message)
			throws RemoteException {
		while (newMessage)
			try {
				wait();
			} catch (InterruptedException e) {
			}
		this.message = message;
		newMessage = true;
		notifyAll();
	}

	public synchronized void getMessageToGame() {
		while (!newMessage)
			try {
				wait();
			} catch (InterruptedException e) {
			}
		Game game = findGame(findUser(message.getUser()));
		if (game != null)
			game.sendMessage(message);
		newMessage = false;
		notifyAll();
	}

	protected void sendMessageTo(Message message, Client user) {
		try {
			user.getInterface().takeMessage(message);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	@Override
	public synchronized void takeChat(Message chat) throws RemoteException {
		while (newChat)
			try {
				wait();
			} catch (InterruptedException e) {
			}
		this.chat = chat;
		newChat = true;
		notifyAll();
	}

	public synchronized void sendChat() {
		while (!newChat)
			try {
				wait();
			} catch (InterruptedException e) {
			}
		Iterator<Client> it = clients.iterator();
		while (it.hasNext())
			try {
				ClientInterface client = it.next().getInterface();
				if (client != null)
					client.takeChat(chat);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		newChat = false;
		notifyAll();
	}

	@Override
	public synchronized void takeButton(ServerButton button, String name)
			throws RemoteException {
		this.button = button;
		user = name;
		newButton = true;
		notifyAll();
	}

	private synchronized void getButtonToGame() {
		while (!newButton)
			try {
				wait();
			} catch (InterruptedException e) {
			}
		Game game = findGame(findUser(user));
		game.sendButton(button);
		newButton = false;
	}

	public void sendButton(ServerButton button, Client user) {
		try {
			user.getInterface().takeButton(button);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public void winner(Client winner, Client loser) {
		winner.addScore();
		try {
			winner.getInterface().takeInfo("SYSTEM: You win!");
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		try {
			loser.getInterface().takeInfo("SYSTEM: You lose!");
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public void draw(Client one, Client two) {
		try {
			one.getInterface().takeInfo("SYSTEM: Draw game!");
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		try {
			two.getInterface().takeInfo("SYSTEM: Draw game!");
		} catch (RemoteException e) {
			e.printStackTrace();
		}

	}

	private class MessageThread extends Thread {
		public void run() {
			while (true)
				getMessageToGame();
		}
	}

	private class ChatThread extends Thread {
		public void run() {
			while (true)
				sendChat();
		}
	}

	private class ButtonThread extends Thread {
		public void run() {
			while (true)
				getButtonToGame();
		}
	}

	public void close() {
		Iterator<Client> it=clients.iterator();
		while(it.hasNext()) {
			ClientInterface client=it.next().getInterface();
			if(client!=null)
				try {
					client.serverClosed();
				} catch (RemoteException e) {
					e.printStackTrace();
				}
		}
		System.exit(1);
	}

	@Override
	public void restart(String name) throws RemoteException {
		Game game=findGame(findUser(name));
		game.restart();
	}

	public void resetButtons(Client client, boolean myTurn) {
		try {
			client.getInterface().resetButtons(myTurn);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

}